/*
** oal_hash.c
**
** CopyRight (C) 2013
**
** 修改记录:
**	2013-01-11: zhouhj 创建
*/
#include <oal_hash.h>
//#include <oal_assert.h>
#include <oal.h>


struct oal_hash_t
{
	/*hash数组 创建时分配内存*/
	oal_list_t *table;
	unsigned size;
	unsigned count;
	void (*freefn)(void *);
};

typedef struct oal_hash_entry_t
{
	oal_list_t list;
	void *key;
	unsigned keylen;
	void *value;
}oal_hash_entry_t;
//  0000 0101
//  0000 0100
//  0000 0111

//   0000 1000
//计算hash值 
// 	times33哈希算法
OAL_INLINE(uint32_t) oal_hash_calc(const void *key,unsigned keylen)
{
	register uint32_t hash = 5381;
	const char *arKey = (const char *)key;

	for (; keylen >= 8; keylen -= 8) {
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
		hash = ((hash << 5) + hash) + *arKey++;
    }
    switch (keylen) {
		case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
		case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
		case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
		case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
		case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
		case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
		case 1: hash = ((hash << 5) + hash) + *arKey++; break;
		case 0: break;
	}
/*
	等价写法
	int i;
	const char *arKey = key;
	for(i=0; i<keylen; i++){
		hash = hash * 33 + arKey[i];
	}
*/
	return hash;
}

//创建一个hash表
OAL_DECL(oal_hash_t *) oal_hash_create(unsigned size)
{
	int i;
	oal_hash_t *ht;

	//分配hash表结构的内存
	void *htmem = oal_malloc(sizeof(oal_hash_t) + size*sizeof(oal_list_t));

	ht = (oal_hash_t *)htmem;

	ht->table = (oal_list_t *)((char *)htmem + sizeof(oal_hash_t));
	ht->size = size;
	ht->count = 0;
	ht->freefn = NULL;

	for ( i = 0; i < size; ++i)
	{
		OAL_LIST_INIT(&ht->table[i]);
	}
	return ht;
}

OAL_DECL(void ) oal_hash_set_free(oal_hash_t *ht,void (*freefn)(void *))
{
	ht->freefn = freefn;
}
static oal_hash_entry_t *oal_hash_entry_get(oal_hash_t *ht,const char *key,unsigned keylen)
{
	oal_list_t *pos;
	unsigned idx ;
	if(oal_hash_count(ht) == 0) {
		return NULL;
	}
	idx = oal_hash_calc(key,keylen) % ht->size;
	oal_list_foreach(pos,&ht->table[idx]){
		oal_hash_entry_t *entry = oal_list_entry(pos,oal_hash_entry_t,list);
		if(keylen == entry->keylen && memcmp(key,entry->key,keylen)==0)
		{
			return entry;
		}
	}
	return NULL;
}
//通过键值查找hash表
OAL_DECL(void *) oal_hash_get(oal_hash_t *ht,const char *key)
{
	unsigned keylen = strlen(key);
	oal_hash_entry_t *entry = oal_hash_entry_get(ht,key,keylen);
	return entry == NULL? NULL : entry->value;
}
static oal_hash_entry_t *oal_hash_entry_create(const char *key,unsigned keylen,void *value)
{
	oal_hash_entry_t *entry ;
	void *emem;
	emem = oal_malloc(sizeof(oal_hash_entry_t)+keylen);
	entry = (oal_hash_entry_t *)emem;
	entry->key = (char *)emem + sizeof(oal_hash_entry_t);
	entry->keylen = keylen;
	entry->value = value;
	memcpy(entry->key,key,keylen);
	return entry;
}
OAL_DECL(void) oal_hash_put(oal_hash_t *ht,const char *key,void *value)
{
	unsigned idx ;
	unsigned keylen = strlen(key);
	idx = oal_hash_calc(key,keylen) % ht->size;
	oal_hash_entry_t *entry = oal_hash_entry_create(key,keylen,value);
	oal_list_add(&ht->table[idx],&entry->list);
	ht->count++;
}

OAL_DECL(unsigned) oal_hash_count(oal_hash_t *ht)
{
	return ht->count;
}

OAL_DECL(void *) oal_hash_remove(oal_hash_t *ht,const char *key)
{
	void *value;
	unsigned keylen = strlen(key);
	oal_hash_entry_t *entry = oal_hash_entry_get(ht,key,keylen);
	if(entry == NULL){
		return NULL;
	}
	oal_list_del(&entry->list);
	value = entry->value;
	oal_free(entry);
	ht->count--;
	return value;
}

OAL_DECL(void) oal_hash_delete(oal_hash_t *ht,const char *key)
{
	void *value = oal_hash_remove(ht,key);
	if(value && ht->freefn){
		ht->freefn(value);
	}
}
OAL_DECL(void) oal_hash_clear(oal_hash_t *ht)
{
	int i;
	for ( i = 0; i < ht->size; ++i)
	{
		oal_list_t *pos,*n;
		oal_hash_entry_t *entry;
		oal_list_foreach_safe(pos,n,&ht->table[i]){
			entry = oal_list_entry(pos,oal_hash_entry_t,list);
			if(ht->freefn){
				ht->freefn(entry->value);
			}
			oal_list_del(&entry->list);
			oal_free(entry);
		}
	}
	ht->count = 0;	
}
OAL_DECL(void) oal_hash_destory(oal_hash_t *ht)
{
	oal_hash_clear(ht);
	oal_free(ht);
}



